package org.e_gold.service.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.log4j.Logger;
import org.e_gold.dao.SettledContractDAO;
import org.e_gold.factory.Manager;
import org.e_gold.factory.export.MonitorThread;
import org.e_gold.model.Contract;
import org.e_gold.model.Event;
import org.e_gold.model.SettledContract;
import org.e_gold.service.ConfigService;
import org.e_gold.service.SettledContractService;
import org.e_gold.service.UserAccountService;
import org.e_gold.util.DateUtil;
import org.e_gold.util.StringUtil;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class SettledContractServiceImpl extends _NavigatorService implements
SettledContractService {

	private static final Logger	logger	= Logger.getLogger(SettledContractServiceImpl.class);

	@Autowired
	private SettledContractDAO	settledContractDAO;

	@Autowired
	private UserAccountService	userAccountService;

	@Autowired
	private MonitorThread monitor;

	@Autowired
	private ConfigService configService;

	private final Integer MAX_PAGE_TO_CHECK = 5;

	private static final String CONTRACT_STATUS_LOST = "Lost";

	@Override
	@Transactional
	public Event getRunningEvent(SettledContract running) {

		Contract contractRunning = running.getContract();
		return contractRunning.getEvent();
	}

	@Override
	public void updateOnlineStatus() {

		try {
			userAccountService.login();

			//decide when to reach online status
			boolean isValidForHooKOnlineService = false;
			List<SettledContract> lstUnSettledContract = getRunningSettledContract();
			if(CollectionUtils.isNotEmpty(lstUnSettledContract)){
				for(SettledContract sc : lstUnSettledContract){
					Event evt = sc.getContract().getEvent();
					if(evt.getPlayTime() > 85){
						isValidForHooKOnlineService = true;
					}
				}
			}
			if(isValidForHooKOnlineService){
				updateOnlineSettled();

				updateOnlineUnsettled();
			}

			// get online balance and synchronize
			userAccountService.updateAmount_Per_Time();
			Float balance = userAccountService.getBalance();
			monitor.offer(balance);
		} catch (Exception e) {
			logger.error(e);
			e.printStackTrace();
		}
	}

	@Override
	public void updateOnlineSettled() {
		// get online settled_contract -- got result
		try {
			parseSettledPage(SETTLED_HISTORY);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void updateOnlineUnsettled() {
		// get online un_settled_contract -- no result
		try {
			parseSettledPage(UN_SETTLED_HISTORY);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Parse settle page contains many settled link: settled & un-settled pages
	 * 
	 * @param hrefPage
	 * @return
	 * @throws Exception
	 * @author Thang Nguyen
	 */
	private void parseSettledPage(String hrefPage)
			throws Exception {
		try {
			Document historyPage = redirectNavigator(hrefPage);
			Elements pageContent = historyPage.select("table.History");
			if(pageContent.size() == 0){
				return;
			}
			Element table = historyPage.select("table.History").get(0);
			Elements rows = table.getElementsByTag("tr");

			//remove title
			int rowSize = rows.size() - 1;

			if(rowSize == 0){
				return;
			}

			//if 10 link active, check 5 only
			int maxPageToCheck = rowSize > MAX_PAGE_TO_CHECK ? MAX_PAGE_TO_CHECK : rowSize;

			for (int i = 1; i <= maxPageToCheck; i++) {
				Element rowToCheck = rows.get(i);
				String contractLink = rowToCheck.select("a").attr("href");
				contractLink = StringUtil.buildUrl(hrefPage, contractLink);
				parseSettleLink(rowToCheck, contractLink);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			logger.error(ex);
		}
	}

	/**
	 * Parse settle link and set status for related settled contract on db.
	 * 
	 * @param hrefLink
	 * @return
	 * @throws Exception
	 * @author Thang Nguyen
	 */
	private void parseSettleLink(Element rowToCheck, String hrefLink) throws Exception {
		try {
			boolean isChanged = false;

			Document doc = redirectNavigator(hrefLink);
			String msg = doc.select("div.msgBlock").html();
			msg = msg.replace("\n", "");
			String[] msgs = msg.split("<br />");
			//			String[] contract = msgs[6].split(" ");

			//Check settledContract in db
			Date insertTimeDate = getInsertTime(rowToCheck);
			//get range
			Date[] insertTimeRange = DateUtil.getMinuteInRange(2, insertTimeDate);
			SettledContract sc = settledContractDAO.getPlacedContract(insertTimeRange);
			if(sc == null){
				return;
			}

			//running or failed
			if(msgs.length == 11){
				String status = msgs[7];
				boolean isLost = StringUtils.equals(status, CONTRACT_STATUS_LOST);

				if(isLost){
					if(sc.getRunning() || sc.getWon()){
						sc.setWon(false);
						sc.setRunning(false);
						isChanged = true;
					}
				}
			}else if(msgs.length == 12){	//won
				if(!sc.getRunning() && sc.getWon()){
					return;
				}
				sc.setWon(true);
				sc.setRunning(false);

				String strWonAmount = msgs[9].split(" ")[1];
				Float wonAmount = Float.valueOf(strWonAmount);
				sc.setResultAmount(wonAmount);
				isChanged = true;
			}
			if(isChanged){

				settledContractDAO.update(sc);
				monitor.offer(sc);
				Manager.match_done(sc);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			logger.error(ToStringBuilder.reflectionToString(ex, ToStringStyle.SHORT_PREFIX_STYLE));
			logger.info(ex);
		}
	}

	private Date getInsertTime(Element settledRow){
		Date insertTime = null;
		try{
			Elements items = settledRow.select("a");
			if(!items.isEmpty() && items.size() == 3){
				String str = items.get(0).text();
				str = str.replace("  ", " ");
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				insertTime = sdf.parse(str);
			}
		}catch(Exception ex){
			logger.error(ToStringBuilder.reflectionToString(ex, ToStringStyle.SHORT_PREFIX_STYLE));
			logger.info(ex);
			ex.printStackTrace();
		}
		return insertTime;
	}

	@Override
	public void traceAndUpdateSettledContract(SettledContract settledContract) {
		try{
			Event e = settledContract.getContract().getEvent();

			List<SettledContract> lst = new ArrayList<SettledContract>();
			SettledContract scExample = new SettledContract();
			Contract ctExample = new Contract();

			ctExample.setEvent(e);
			scExample.setContract(ctExample);

			List<SettledContract> lstEx = settledContractDAO
					.findByExample(scExample);

			for(SettledContract sc : lstEx){
				if(!sc.getWon().equals(settledContract.getWon())
						||!sc.getRunning().equals(settledContract.getRunning())){
					sc.setWon(settledContract.getWon());
					sc.setRunning(settledContract.getRunning());
					monitor.offer(sc);
					lst.add(sc);
				}

				if(!settledContract.getRunning()){
					Manager.workingContract.remove(sc.getContract());
				}
			}
			if(CollectionUtils.isNotEmpty(lst)){
				logger.info("Updating status [" + settledContract.getWon() + "]: for " + e.getName() + " (" + lst.size() + ")");
				settledContractDAO.update(lst);
			}
		}catch(Exception ex){
			logger.error(ex);
		}
	}

	@Override
	@Transactional
	public void updateRunningToFalse(SettledContract contract) {
		settledContractDAO.updateRunningToFalse(contract);

	}

	@Override
	public List<SettledContract> getRunningSettledContract() {

		return settledContractDAO.getListRunningSettledContract();
	}

}
